package org.ns.framework.webmvc.interceptor;

import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.logging.log4j.ThreadContext;
import org.ns.basics.commons.lang.StringUtils;
import org.ns.framework.webmvc.ResponseEntityKey;
import org.ns.framework.webmvc.ResponseEntityKey.DefaultResponseEntityKey;
import org.ns.framework.webmvc.ResponseEntityResultAdapter;
import org.ns.framework.webmvc.anns.ApiController;
import org.ns.framework.webmvc.anns.LoginVerify;
import org.ns.framework.webmvc.anns.NoLoginVerify;
import org.ns.framework.webmvc.interceptor.CorsConfig.DefaultCorsConfig;
import org.ns.framework.webmvc.support.ResponseEntityKeyUtils;
import org.ns.framework.webmvc.verify.DefaultTokenCodec;
import org.ns.framework.webmvc.verify.LoginVerifyFunction;
import org.ns.framework.webmvc.verify.LoginVerifyService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;


public class RequestInterceptor implements HandlerInterceptor, ApplicationContextAware  {
	
	private static final Logger log = LoggerFactory.getLogger(RequestInterceptor.class);

    @Autowired
	LoginVerifyService loginVerifyService;
    
    @Autowired(required = false)
	ResponseEntityKey responseEntityKey = DefaultResponseEntityKey.defaultInstance;
    @Autowired(required = false)
    RequestIdGenerator requestIdGenerator = RequestIdGenerator.DefaultRequestIdGenerator.defaultInstance;
    @Autowired(required = false)
    @Qualifier("globalResultAdapter")
    ResponseEntityResultAdapter globalResultAdapter;
    
    ApplicationContext applicationContext;
    
    @Autowired(required = false)
    CorsConfig corsConfig = new DefaultCorsConfig();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public void setLoginVerifyService(LoginVerifyService loginVerifyService) {
        this.loginVerifyService = loginVerifyService;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    	requestTrace(request, response);
    	
    	if (corsConfig == null) {
    		corsConfig = CorsConfig.defaultInstance;
    	}
    	corsConfig.setCors(request, response);
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            configCurrentResponseFormat(request, response, handlerMethod);
            if (!loginVerify(request, response, handlerMethod)) {
                return false;
            }
        }
      //org.springframework.web.servlet.handler.AbstractHandlerMapping.PreFlightHandler
        if (handler.getClass().getSimpleName().equals("PreFlightHandler")) {
        }
        if (handler instanceof ResourceHttpRequestHandler) {

        }

        return true;
    }
    
    // ApiController 缓存
    Map<Class<?>, Optional<ApiController>> controllers = new ConcurrentHashMap<>();
    
    private Optional<ApiController> getApiController(HandlerMethod handlerMethod) {
    	Class<?> beanType = handlerMethod.getBeanType();
    	Optional<ApiController> apiControllerOt = controllers.get(beanType);
    	if (apiControllerOt == null) {
    		ApiController apiController = AnnotatedElementUtils.findMergedAnnotation(beanType, ApiController.class);
    		apiControllerOt = Optional.ofNullable(apiController);
    		controllers.put(beanType, apiControllerOt);
    	}
    	return apiControllerOt;
    }
   
    // 配置当前应答格式
	private void configCurrentResponseFormat(HttpServletRequest request, HttpServletResponse response,
			HandlerMethod handlerMethod) {

		request.setAttribute(ResponseEntityKey.KEY, responseEntityKey);  // 默认应答实体名字. 低优先级.
		request.setAttribute(ResponseEntityResultAdapter.KEY, globalResultAdapter); // 结果适配器 优先级更大.
		
		Optional<ApiController> apiControllerOt = getApiController(handlerMethod);
		if (apiControllerOt != null && apiControllerOt.isPresent()) {
			String resultAdapterName = apiControllerOt.get().resultAdapterName();
			if (StringUtils.isNotBlank(resultAdapterName) && applicationContext.containsBean(resultAdapterName)) {
				try {
					ResponseEntityResultAdapter resultAdapter = applicationContext.getBean(resultAdapterName,
							ResponseEntityResultAdapter.class);
					request.setAttribute(ResponseEntityResultAdapter.KEY, resultAdapter);
				} catch (Exception e) {
					log.error("bean 不存在 " + resultAdapterName, e);
				}
			}
			Class<? extends ResponseEntityKey> entityKeyType = apiControllerOt.get().entityKey();
			if (entityKeyType != ResponseEntityKey.class && !entityKeyType.isInterface()) {
				ResponseEntityKey entityKey = ResponseEntityKeyUtils.getSingleton(entityKeyType);	
				if (entityKey != null) {
					request.setAttribute(ResponseEntityKey.KEY, entityKey);
				}
			}
		}
	}
    
    /**
     * 登录验证
     */
    private boolean loginVerify(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    	request.setAttribute("LoginVerifyService", loginVerifyService);
        if (handlerMethod.hasMethodAnnotation(NoLoginVerify.class)) {
            return true;
        }
        Object status = request.getAttribute(DefaultTokenCodec.TOKEN_STATUS_KEY);
		if (status == null) {
			// 没做过登陆验证
		} else if ("1".equals(status)) {
			return true;
		} else {
			return false;
		}
        
        Class<?> beanType = handlerMethod.getBeanType();
        LoginVerify loginVerify = AnnotatedElementUtils.findMergedAnnotation(beanType, LoginVerify.class);
        if (loginVerify == null) {
            loginVerify = handlerMethod.getMethodAnnotation(LoginVerify.class);
        }
        // 不必须登陆则不校验登陆状态
        if (loginVerify == null) {
            return true;
        }
        // 登陆验证函数
        LoginVerifyFunction loginVerifyFunction = loginVerifyService.getLoginVerifyFunction(handlerMethod);
        
        if (loginVerifyFunction == null) {
        	// 不存在登陆验证函数
        	log.warn("无法使用LoginVerify的验证功能, 因为请求拦截器不存在登陆验证函数! ");
        	return false;
        }

        // 需要校验
        boolean loginState = loginVerifyFunction.verifyLogin(loginVerify, request, response, handlerMethod);
        // 登录失败
        if (!loginState && loginVerify.mustLogin()) {
            LoginVerifyFailResponse loginVerifyFailResponse = loginVerifyService.getLoginVerifyFailResponse(handlerMethod.getMethod());
            
            // 错误输出方式, 分为 API输出和PAGE输出.
            if (AnnotatedElementUtils.hasAnnotation(handlerMethod.getBeanType(), ResponseBody.class)
                || handlerMethod.hasMethodAnnotation(ResponseBody.class)) {
                // API 方式输出
                loginVerifyFailResponse.doApi(request, response, handlerMethod);
            } else {
                // PAGE 方式输出
                loginVerifyFailResponse.doPage(request, response, handlerMethod);
            }
            request.setAttribute(DefaultTokenCodec.TOKEN_STATUS_KEY, "0"); // 
        } else {
        	request.setAttribute(DefaultTokenCodec.TOKEN_STATUS_KEY, "1"); // 
        	loginState = true;
        }
        return loginState;
    }
  
    private void requestTrace(HttpServletRequest request, HttpServletResponse response) throws Exception {
    	try {
    		String requestId = requestIdGenerator.getRequestId(request);
        	request.setAttribute("requestId", requestId);
        	ThreadContext.put("requestId", requestId);
		} catch (Exception e) {
			log.error("请求追踪", e);
		}
    }
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}
